package genetica.cromosomas;

import genetica.cromosomas.evaluadores.Evaluador;
import genetica.cromosomas.fenotipos.Fenotipo;
import genetica.cromosomas.genes.Gen;
import genetica.excepciones.ExcepcionGenetica;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

public abstract class Cromosoma implements Comparable<Cromosoma> , Cloneable{
	
	protected ArrayList<Gen> 	genes;
	protected ArrayList<Fenotipo>	fenotipo;
	protected double 				aptitud;
	protected double 				puntuacion;
	protected double 				punt_acumu;
	protected static ArrayList<Integer> 	longGenes = null;
	protected static Evaluador evaluador;

	protected static Double techo = null;
	protected static Double suelo = null;	
	protected static Double minProb = 0.10;
	protected static boolean maximizar = true;
	protected static Comparator<Cromosoma> comparadorSinIgualdad = new ComparatorCromosoma();
	public static void setTecho(double techo) {
		Cromosoma.techo = techo;
	}
	public static void setSuelo(double suelo) {
		Cromosoma.suelo = suelo;
	}
	public static void setMinProb(double minProb){
		Cromosoma.minProb = minProb;
	}
	/**
	 * La altitud normalizada devuelve, si estan definidos suelo y techo, un valor de la aptitud que usa
	 * el techo y el suelo, este valor supone ser positivo. No realiza calculos de aptitud
	 * @return
	 */
	public Double getAptitudNormalizada() {
		if (Cromosoma.techo.equals(Cromosoma.suelo)){
			return aptitud;
		}
		// el que sea el suelo va a tener una aptitud 0, sumamos un poquito (la probabilidad minima de cada objeto
		if (Cromosoma.maximizar){
			if (suelo != null && techo != null)
				return aptitud - Cromosoma.suelo + (Cromosoma.techo - Cromosoma.suelo) * Cromosoma.minProb;
		} else {
			if (suelo != null && techo != null)
				return Cromosoma.techo - aptitud + (Cromosoma.techo - Cromosoma.suelo) * Cromosoma.minProb;
		}
		throw new Error("caca");
	}
	/**
	 * Devuelve el valor de la aptitud. No la calcula este deve ser calcualda con la funcion evalua
	 * @return
	 */
	public Double getAptitud(){
		return aptitud;
	}
	
	public ArrayList<Fenotipo> getFenotipo() {
		return fenotipo;
	}
	

	public static Evaluador getEvaluador() {
		return evaluador;
	}

	public static void setEvaluador(Evaluador evaluador) {
		Cromosoma.evaluador = evaluador;
	}

	public Cromosoma(){
		if (longGenes == null)
			Cromosoma.longGenes = new ArrayList<Integer>();
		this.genes = new ArrayList<Gen>();
		this.fenotipo = new ArrayList<Fenotipo>();
	}
	
	public ArrayList<Gen> getGenes() {
		return genes;
	}

	public void setGenes(ArrayList<Gen> genes) {
		this.genes = genes;
	}

	public static ArrayList<Integer> getLongGenes() {
		return Cromosoma.longGenes;
	}

	public static void setLongGenes(ArrayList<Integer> longGenes) {
		Cromosoma.longGenes = longGenes;
	}

	public abstract void calcularFenotipo();

	public void setAptitud(Double aptitud) {
		this.aptitud = aptitud;
	}

	public Double getPuntuacion() {
		return puntuacion;
	}

	public void setPuntuacion(Double puntuacion) {
		this.puntuacion = puntuacion;
	}

	public Double getPuntAcum() {
		return punt_acumu;
	}

	public void setPuntAcum(Double punt_acum) {
		this.punt_acumu = punt_acum;
	}

	public int longGen(){
		
		return this.genes.size();
	}
	/**
	 * Te da la evaluacion sin minimo y maximo
	 * @return
	 * @throws ExcepcionGenetica
	 */
	public final void evalua() {
		
		this.aptitud = evaluador.evalua(this.fenotipo);
	}
	public static Integer binToDec(List<Boolean> gen) {
		if (gen == null)
			return null;

		Integer acumulado = 0;
		for (Boolean b : gen) {
			acumulado <<= 1;
			acumulado += (b) ? 1 : 0;
			
		}
		return acumulado;
	}
	// TODO comprobar quelo hace bien
	public static boolean[] decTiBin(int entrada) {
		int tam = Double.valueOf(Math.ceil(Math.log(entrada)/Math.log(2))).intValue();
		boolean [] result = new boolean[tam];
		for (int i = 0;i<tam;i++){
				result[i] = ((entrada % 2) == 1);
				entrada >>= 1;
		}
		return result;
	}
//	public void setMaximizar(Boolean maximizar){
//		this.maximizar = maximizar;
//	}
	public static Boolean getMaximizar(){
		return Cromosoma.maximizar;
	}
	public static void setMaximizar(boolean maximizar){
		Cromosoma.maximizar = maximizar;
	}
	
	@Override
	public int compareTo(Cromosoma o) {
//		calcularFenotipo();
//		o.calcularFenotipo();
//		evalua();
//		o.evalua();
		if (Cromosoma.maximizar)
			return Double.compare(aptitud, o.aptitud);
		return Double.compare(o.aptitud,aptitud);
	}
	
	@Override
	public abstract Cromosoma clone();
	
	public boolean check (){
		return true;
	}
	
	static class ComparatorCromosoma implements Comparator <Cromosoma>{

		@Override
		public int compare(Cromosoma o1, Cromosoma o2) {
			int result = o1.compareTo(o2);
			if (result != 0){
				return result;
			}else{
				Random r = new Random();
				if (r.nextBoolean())
					return 1;
			}
			return -1;
		}
		
	}
	
	public static Comparator<Cromosoma> getComparadorSinIgualdad(){
		return Cromosoma.comparadorSinIgualdad;
	}
	
	public abstract void codifica();
	public abstract void decodifica();

	
}
